package paper.db;

import java.util.*;
import java.util.regex.*;
import java.io.*;


import bibtex.dom.BibtexEntry;
import bibtex.dom.BibtexFile;
import bibtex.dom.BibtexString;
import bibtex.parser.BibtexParser;
import bibtex.parser.ParseException;
import bibtex.expansions.CrossReferenceExpander;
import bibtex.expansions.ExpansionException;
import bibtex.expansions.MacroReferenceExpander;
import bibtex.expansions.PersonListExpander;

import paper.db.Paper;
import paper.db.PaperCache;

public class BibTex {
    
    static HashMap<String,Paper> titleIndex = new HashMap<String,Paper>();
    static HashMap<String,Paper> keyIndex = new HashMap<String,Paper>();
    static Vector<Paper> allEntries = new Vector<Paper>();
    static public PaperCache cache = new PaperCache();

    static public void register(Paper be) {
        titleIndex.put(be.flatTitle, be);
        keyIndex.put(be.key, be);
        allEntries.add(be);
    }


    public BibTex() {
    }
    
    static String getField(BibtexEntry e, String s) {
        BibtexString x = (BibtexString) e.getFieldValue(s);
        if (x != null) {
            return x.getContent();
        }
        return "";
    }

    static public void load(String name) {
        try {
            FileReader in = new FileReader(name);
            BibtexParser parser = new BibtexParser(false);
            BibtexFile file=new BibtexFile();
            parser.parse(file,in);
            MacroReferenceExpander expander =
                new MacroReferenceExpander(true, true, false, false);
            expander.expand(file);

            for(Iterator it=file.getEntries().iterator();it.hasNext();){
                Object potentialEntry = it.next();
                if(!(potentialEntry instanceof BibtexEntry)) continue;
                BibtexEntry e = (BibtexEntry) potentialEntry;
                String a = getField(e,"author");
                String t = getField(e,"title");
                if (a.equals("") || t.equals("")) continue;

                Paper duplicate = find(a, t);
                if (duplicate != null) {
                    System.err.println("Ignored duplicate: " + duplicate.bibTexKey + " " + duplicate.reason);
                    continue;
                }
                String url = getField(e,"url");
                String abstractX = getField(e,"abstract");
                String address  = getField(e,"address ");
                String booktitle = getField(e,"booktitle");
                String editor = getField(e,"editor");
                String howpublished = getField(e,"howpublished");
                String institution = getField(e,"institution");
                String isbn = getField(e,"isbn");
                String issn = getField(e,"issn");
                String issue = getField(e,"issue");
                String journal = getField(e,"journal");
                String month = getField(e,"month");
                String number = getField(e,"number");
                String pages = getField(e,"pages");
                String publisher = getField(e,"publisher");
                String school = getField(e,"school");
                String volume = getField(e,"volume");
                String year = getField(e,"year");
                
                Paper be = new Paper(e.getEntryKey(), a, "", t, abstractX,address,booktitle,editor,howpublished,institution,isbn,issn,issue,journal,month,number,pages,publisher,school,volume,year,url, cache);
                register(be);
                be.commitEntry("Bib file " + name);
            }
        } catch (FileNotFoundException e) {
            System.err.println("File '"+name+"' not found.");
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (Exception e) {
            System.err.println("Shit  " + e);
            e.printStackTrace();
        }
    }

    static public void loadDB() {
        Paper.loadAll(null);                // Don't use a PaperCache if control comes through here!!!!!!!!
    }
    
    static class Comparator {
        int x, y;
        Comparator(int x, int y) {
            this.x = x;
            this.y = y;
        }
        
        Comparator xplus1() {
            return new Comparator(x+1,y);
        }
        
        Comparator yplus1() {
            return new Comparator(x,y+1);
        }
    }

    static double fuzzyMatch(String x, String y) {
        if (x.length() == 0 && y.length() == 0) return 0.0;
        if (x.length() == 0) return 1.0;
        if (y.length() == 0) return 1.0;
        int limit = 10;
        int[][] dist = new int[x.length()][y.length()];
        LinkedList<Comparator> oneplus, tobedone;
        int best = 1;
        Comparator start = new Comparator(0,0);
        Comparator current;
        tobedone = new LinkedList<Comparator>();
        tobedone.add(0,start);
        dist[0][0] = best;

        while(true) {
            oneplus = new LinkedList<Comparator>();
            while (!tobedone.isEmpty()) {
                current = tobedone.remove(0);
                if (current.x == x.length()-1 && current.y == y.length()-1) {
                    return best/(double)(x.length() + y.length());
                }                
                while (true) {
//                    System.out.println("Vec " + current.x + " " + current.y + " setting " + best);
                    if (current.x < x.length()-1 && dist[current.x+1][current.y] == 0) {
                        dist[current.x+1][current.y] = best+1;
                        oneplus.add(0,current.xplus1());
                    }
                    if (current.y < y.length()-1 && dist[current.x][current.y+1] == 0) {
                        dist[current.x][current.y+1] = best+1;
                        oneplus.add(0,current.yplus1());
                    }
                    if (x.charAt(current.x) != y.charAt(current.y)) {
                        break;
                    }
                    current.x++;
                    current.y++;
                    
                    if (current.x >= x.length()) {
                        break; //return y.length() - current.y + best;
                    }
                    if (current.y >= y.length()) {
                        break; //return x.length() - current.x + best;
                    }
                    if (dist[current.x][current.y] == 0) {
                        dist[current.x][current.y] = best;
                    } else {
                        break;
                    }
                }
            }
            tobedone = oneplus;
            best++;
            if (best == limit) {
                return 1.0;
            }
        }
    }

    static public Paper find(String author, String title) {
        String flatTitle = Paper.cleanUp(title);
        Paper e = titleIndex.get(flatTitle);
//        System.out.println("Finding " + flatTitle);
        if (e != null) {
//            System.out.println("Found locally " + flatTitle);
            return e;
        }
        HashSet<Paper> hp  = Paper.loadOnFlatTitle(flatTitle,cache);          // Don't use a PaperCache if control comes through here!!!!!!!!
        Iterator<Paper> ip = hp.iterator();
        if (ip.hasNext()) {
//            System.out.println("Found remotely " + flatTitle);
            return ip.next();                      // fixme, not unique.
        }
//        System.out.println("No present " + flatTitle);
        if (false) {
            double best = 1.0;
            Paper beste = null;
            for (int i = 0; i < allEntries.size(); i++) {
                e = allEntries.get(i);
                double c = fuzzyMatch(e.flatTitle, flatTitle);
                if (c < best) {
                    best = c;
                    beste = e;
                }
            }        
            if (beste != null && best < 0.05) {
                System.err.println("Found fuzzy match");
                System.err.println("  Search " + title);
                System.err.println("  Found  " + beste.title);
                return beste;
            }
        }
        return null;
    }

    static public Paper findKey(String key) {
        return keyIndex.get(key);
    }

    static public Paper newPaper(String a, String e, String t, String y, String rest) {
        Paper be = new Paper("", a, e, t, "", "", "", "", rest, "", "", "", "", "", "", "", "", "", "", "", y, "", cache);
        register(be);
        return be;
    }

    static public int stats(PrintWriter out) {
        int n = 0;
        int hasCitations = 0;
        for (int i = 0; i < allEntries.size(); i++) {
            Paper e = allEntries.get(i);
            if (e.interesting) {
                if (e.citations.size() != 0) {
                    hasCitations++;
                }
                n++;
            }
        }        
        Paper[] allE = new Paper[n];
        for (int i = 0, j = 0; i < allEntries.size(); i++) {
            Paper e = allEntries.get(i);
            if (e.interesting) {
                allE[j++] = e;
            }
        }        
        out.println("Out of " + n + " referenced entries, " + hasCitations + " were in the dataset. <ol>");
        int index = 0;
        java.util.Arrays.sort(allE);
        for (int i = 0; i < n; i++) {
            Paper e = allE[i];
            e.index = i;
            e.makeBibTexFile();
            out.print("<li><tt><a href='bibtex-" + e.key + ".html'>" + e.key + "</a></tt>: ");
            out.print("referenced by " + e.references.size() + " papers");
            if (!e.bibTexKey.equals("")) {
                out.print(", aka " + e.bibTexKey);
            }
            if (e.citations.size() != 0) {
                out.print(", " + e.citations.size() + " citations, ");
                out.print("<a href='data-" + e.key + ".html'>Data file</a>");
            }
            out.println(".</li>");
        }        
        out.println("</ol>Adjacency matrix; a 1 in row <em>r</em> column <em>c</em> means that paper <em>c</em> refers to paper <em>r</em>. The correspondence between papers and row/columns is listed above.<pre>");
        out.println("[");
        for (int i = 0; i < n; i++) {
            Paper e = allE[i];
            int[] row = new int[n];
            for(Iterator<Paper> j = e.references.iterator(); j.hasNext();){
                Paper be = j.next();
                row[be.index] = 1;
            }
            for (int j = 0; j<n; j++) {
                out.print(" " + row[j]);
            }
            out.println(";");
        }        
        out.println("]</pre>");
        return n;
    }

    static public void makeDotCocite(PrintWriter out)
    {
        out.println("digraph{");
        HashMap<String,Integer> hm = new HashMap<String,Integer>();
        for (int i = 0; i < allEntries.size(); i++) {
            Paper e = allEntries.get(i);
            if (e.interesting) {
                //out.println(e.key + " [label=\"" + e.key.replaceAll("\n"," ") + "\"];");
                for(Iterator<Paper> j = e.citations.iterator(); j.hasNext();){
                    Paper be = j.next();
                    for(Iterator<Paper> k = be.references.iterator(); k.hasNext();){
                        String fromKey = k.next().key;
                        String key1 = clean(fromKey), key2 = clean(e.key);
                        if( key1.compareTo(key2) > 0 )
                        {
                            String t = key1;
                            key1 = key2;
                            key2 = t;
                        }
                        if( key1.equals(key2) )
                            continue;
                        String entry = key1 + " -> " + key2 + " [dir=none";
                        Integer a = hm.get(entry);
                        if( a==null )
                            a = new Integer(0);
                        a = new Integer(a+1);
                        hm.put(entry,a);
                    }
                }
            }
        }        
        for(Iterator<String> i = hm.keySet().iterator(); i.hasNext();)
        {
            String x = i.next();
            Integer a = hm.get(x);
            out.println(x + ",weight=" + (1.0/(float)a) + "];");
        }
        
        out.println("}");
    }

    static public String clean(String x)
    {
        return "x" + x.replaceAll("[-\\._]","");
    }

    static public void makeDotAdj(PrintWriter out)
    {
        out.println("digraph{");
        for (int i = 0; i < allEntries.size(); i++) {
            Paper e = allEntries.get(i);
            if (e.interesting) {
                out.println(e.key + " [label=\"" + e.title + "\"];");
                for(Iterator<Paper> j = e.references.iterator(); j.hasNext();){
                    String fromKey = j.next().key;
                    out.println(fromKey + " -> " + e.key + ";");
                }
            }
        }        
        out.println("}");
    }
}
